package pacman.protocol;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.regex.Pattern;

import maze.Coordinate;
import maze.Direction;
import maze.Location;
import maze.Maze;
import pacman.Game;
import pacman.GameInformation;

public class CommandParser {

	private final boolean VERBOSE = false;

	private Game game;

	public CommandParser(Game game) {
		this.setGame(game);
	}

	/**
	 * Parses all the info from a maze-file. Initializes the maze, sets the
	 * players on the possible positions and sets pacman on the given position
	 */
	public GameInformation parseFile(File file) throws IOException {
		return new GameInformation(file);
	}
	

	/**
	 * Reads the maze.txt file and uses the BARCODE command to add barcodes
	 */
	public static void addBarcodes(File file, Maze maze) throws IOException {
		FileInputStream fstream = new FileInputStream(file);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;
		while ((strLine = br.readLine()) != null) {
			String[] parts = partitionStringFile(strLine);
			if (parts[1].equals("BARCODEAT")) {
				String bc = Integer.toBinaryString(Integer.parseInt(parts[3])*2);
				int[] coords = getCoordsFromString(parts[2]);
				;
				int direction = Integer.parseInt(parts[4]);
				while (bc.length() < 8) {
					bc = 0 + bc;
				}
				if (direction == 3 || direction == 2) {
					// String reversen als ze niet in de richten zijn
					StringBuffer reverser = new StringBuffer(bc);
					// Die overeenkomt zoals nodig voor addBarcode
					bc = new String(reverser.reverse());
				}
				
				if (direction == 1 || direction == 3) {
					// Strings zijn al gereversed dus voor verticale richtingen
					maze.addBarcode(new Coordinate(coords[0], coords[1]), bc, 1);
				} else {
					maze.addBarcode(new Coordinate(coords[0], coords[1]), bc, 0);
				}
			}
		}
	}

	/**
	 * Reads the maze.txt file and uses the PACMAN command to give Pacman it's
	 * starting position
	 */
	public static Coordinate givePacmanPosition(File file) throws IOException {
		FileInputStream fstream = new FileInputStream(file);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;
		Coordinate pacmanCoordinate = null;
		while ((strLine = br.readLine()) != null) {
			String[] parts = partitionStringFile(strLine);
			if (parts[1].equals("PACMAN")) {
				int[] coords = getCoordsFromString(parts[2]);
				pacmanCoordinate = new Coordinate(coords[0], coords[1]);
			}
		}
		return pacmanCoordinate;
	}

	/**
	 * Reads the maze.txt file and uses the POSITION commands to create the new
	 * players and give them their starting positions in the new maze.
	 * 
	 * @param ownPosition
	 * 		Indicates what position you want. (Zero-indexed)
	 */
	public static Coordinate [] givePlayersPositions(File file, int ownPosition) throws IOException {
		FileInputStream fstream = new FileInputStream(file);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;
		int playersWithPositions = 0;
		Coordinate [] playerPositions = new Coordinate[4];
		while ((strLine = br.readLine()) != null) {
			String[] parts = partitionStringFile(strLine);
			if (parts[1].equals("POSITION")) {
				int[] coords = getCoordsFromString(parts[2]);
				playerPositions[playersWithPositions] = new Coordinate(coords[0], coords[1]);
				playersWithPositions++;
			}
		}
		
		return replacePositions(ownPosition, playerPositions);
	}

	private static Coordinate[] replacePositions(int ownPosition,Coordinate[] playerPositions) {
		Coordinate temp = playerPositions[ownPosition];
		playerPositions[ownPosition] = playerPositions[0];
		playerPositions[0] = temp;
		return playerPositions; 
	}

	/**
	 * Reads the maze.txt file and uses the DISCOVER commands to create a maze
	 */
	public static Maze getMaze(File file) throws NumberFormatException, IOException {
		FileInputStream fstream = new FileInputStream(file);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		String strLine;
		Maze maze = new Maze(6);
		while ((strLine = br.readLine()) != null) {
			String[] parts = partitionStringFile(strLine);
			if (parts[1].equals("DISCOVER")) {
				int[] coords = getCoordsFromString(parts[2]);
				int n = Integer.parseInt(parts[3]);
				maze.write(coords[0], coords[1], Direction.NORTH, (char) (2 - n), true);
				int o = Integer.parseInt(parts[4]);
				maze.write(coords[0], coords[1], Direction.EAST, (char) (2 - o), true);
				int z = Integer.parseInt(parts[5]);
				maze.write(coords[0], coords[1], Direction.SOUTH, (char) (2 - z), true);
				int w = Integer.parseInt(parts[6]);
				maze.write(coords[0], coords[1], Direction.WEST, (char) (2 - w), true);
			}
		}
		in.close();
		return maze;
	}

	public void parseCommand(String command) throws IOException {
		//TODO:protocolhandler gebruiken voor NAME en JOIN
		String[] parts = partitionStringCommand(command);
		if (parts[0].equals("JOIN")) {
			game.getProtocolHandler().processJOIN();
			if (VERBOSE) {
				System.out.println("JOIN");
			}

		} else if (parts[1].equals("NAME")) {
			game.getProtocolHandler().processNAME(parts[0], // Name
					parts[2]); // Version
			if (VERBOSE) {
				System.out.println("NAME\n" + "Name: " + parts[0] + "\nVersion: " + parts[2]);
			}
		} else if (parts[1].equals("RENAME")) {
			game.getProtocolHandler().processRENAME(parts[0], // Name
						parts[2]); // Version
				if (VERBOSE) {
					System.out.println("RENAME\n" + "Name: " + parts[0] + "\nVersion: " + parts[2]);
				}
		} else if (parts[1].equals("POSITION")) {
			int[] coords = getCoordsFromString(parts[2]);
			game.getProtocolHandler().processPOSITION(parts[0], // Name
					coords[0], // X
					coords[1]); // Y
			if (VERBOSE) {
				System.out.println("POSITION\n" + "Name: " + parts[0] + "\nX: " + coords[0] + "\nY: " + coords[1]);
			}

		} else if (parts[1].equals("REPOSITION")) {
			int[] coords = getCoordsFromString(parts[2]);
			game.getProtocolHandler().processREPOSITION(parts[0], // Name
					coords[0], // X
					coords[1]); // Y
			if (VERBOSE) {
				System.out.println("REPOSITION\n" + "Name: " + parts[0] + "\nX: " + coords[0] + "\nY: " + coords[1]);
			}

		} else if (parts[1].equals("DISCOVER")) {
			int[] coords = getCoordsFromString(parts[2]);
			game.getProtocolHandler().processDISCOVER(parts[0], // Name
					coords[0], // X
					coords[1], // Y
					Integer.parseInt(parts[3]), // NORTH
					Integer.parseInt(parts[4]), // EAST
					Integer.parseInt(parts[5]), // SOUTH
					Integer.parseInt(parts[6])); // WEST
			if (VERBOSE) {
				System.out.println("REDISCOVER\n" + "Name: " + parts[0] + "\nX: " + coords[0] + "\nY: " + coords[1] + "\nNORTH: " + Integer.parseInt(parts[3]) + "\nEAST: " + Integer.parseInt(parts[4])
						+ "\nSOUTH: " + Integer.parseInt(parts[5]) + "\nWEST: " + Integer.parseInt(parts[6]));
			}

		} else if (parts[1].equals("REDISCOVER")) {
			int[] coords = getCoordsFromString(parts[2]);
			game.getProtocolHandler().processREDISCOVER(parts[0], // Name
					coords[0], // X
					coords[1], // Y
					Integer.parseInt(parts[3]), // NORTH
					Integer.parseInt(parts[4]), // EAST
					Integer.parseInt(parts[5]), // SOUTH
					Integer.parseInt(parts[6])); // WEST
			if (VERBOSE) {
				System.out.println("DISCOVER\n" + "Name: " + parts[0] + "\nX: " + coords[0] + "\nY: " + coords[1] + "\nNORTH: " + Integer.parseInt(parts[3]) + "\nEAST: " + Integer.parseInt(parts[4])
						+ "\nSOUTH: " + Integer.parseInt(parts[5]) + "\nWEST: " + Integer.parseInt(parts[6]));
			}

		} else if (parts[1].equals("BARCODEAT")) {
			int [] coords =  getCoordsFromString(parts[2]);
			game.getProtocolHandler().processBARCODEAT(parts[0], // Name
					new Coordinate(coords[0], coords[1]),
					Integer.parseInt(parts[3]), // Code
					Integer.parseInt(parts[4])); // Direction
			if (VERBOSE) {
				System.out.println("BARCODEAT\n" + "Name: " + parts[0] + "\nCoordinate: " + coords[0] + "," + coords[1] + "\nCode: " + Integer.parseInt(parts[3]) + "\nDirection: " + Integer.parseInt(parts[4]));
			}

		} else if (parts[1].equals("UNDOBARCODE")) {
			int [] coords =  getCoordsFromString(parts[2]);
			game.getProtocolHandler().processUNDOBARCODE(parts[0], // Name
					new Coordinate(coords[0], coords[1]));
			if (VERBOSE) {
				System.out.println("UNDOBARCODE\n" + "Name: " + parts[0] + "\nCoordinate: " + coords[0] + "," + coords[1]);
			}

		}else if (parts[1].equals("REBARCODEAT")) {
			int [] coords =  getCoordsFromString(parts[2]);
			game.getProtocolHandler().processREBARCODEAT(parts[0], // Name
					new Coordinate(coords[0], coords[1]),
					Integer.parseInt(parts[3]), // Code
					Integer.parseInt(parts[4])); // Direction
			if (VERBOSE) {
				System.out.println("REBARCODEAT\n" + "Name: " + parts[0] + "\nCoordinate: " + coords[0] + "," + coords[1] + "\nCode: " + Integer.parseInt(parts[3]) + "\nDirection: " + Integer.parseInt(parts[4]));
			}

		} else if (parts[1].equals("PACMAN")) {
			int[] coords = getCoordsFromString(parts[2]);
			game.getProtocolHandler().processPACMAN(parts[0], // Name
					coords[0], // X
					coords[1]); // Y
			if (VERBOSE) {
				System.out.println("PACMAN\n" + "X: " + coords[0] + "\nY: " + coords[1]);
			}

		} else if (parts[1].equals("REPACMAN")) {
			int[] coords = getCoordsFromString(parts[2]);
			game.getProtocolHandler().processREPACMAN(parts[0], // Name
					coords[0], // X
					coords[1]); // Y
			if (VERBOSE) {
				System.out.println("REPACMAN\n" + "X: " + coords[0] + "\nY: " + coords[1]);
			}

		} else if (parts[1].equals("CAPTURED")) {
			game.getProtocolHandler().processCAPTURED(parts[0]); // Name
			if (VERBOSE) {
				System.out.println("CAPTURED\n" + "Name: " + parts[0]);
			}

		} else if (parts[1].equals("PLAN")) {
			ArrayList<Location> plan = new ArrayList<Location>();
			for (int i = 2; i < parts.length; i++) {
				int[] coords = getCoordsFromString(parts[i]);
				plan.add(new Location(coords[0], coords[1]));
			}
			game.getProtocolHandler().processPLAN(parts[0], plan);
			if (VERBOSE) {
				System.out.println("PLAN\n" + "Name: " + parts[0]);
				for (Location loc : plan) {
					System.out.println("Step " + plan.indexOf(loc) + ": " + loc.toString());
				}
			}

		} else if (parts[1].equals("CANCELPLAN")) {
			game.getProtocolHandler().processCANCELPLAN(parts[0]);
			if (VERBOSE) {
				System.out.println("CANCELPLAN\n" + "Name: " + parts[0]);
			}

		} else if (parts[1].equals("SHOWMAP")) {
			game.getProtocolHandler().processSHOWMAP(parts[2]);
			if (VERBOSE) {
				System.out.println("SHOWMAP\n" + "Name: " + parts[0] + "\nDestination: " + parts[2]);
			}

		} else if (parts[1].equals("PING")) {
			game.getProtocolHandler().processPING(parts[0], // Name
					parts[2], // Destination
					parts[3]); // Message
			if (VERBOSE) {
				System.out.println("PING\n" + "Name: " + parts[0] + "\nDestination: " + parts[2] + "\nMessage: " + parts[3]);
			}

		} else if (parts[1].equals("PONG")) {
			game.getProtocolHandler().processPONG(parts[0], // Name
					parts[2], // Destination
					parts[3]); // Message
			if (VERBOSE) {
				System.out.println("PONG\n" + "Destination: " + parts[2] + "\nMessage: " + parts[3]);
			}
		
		} else{
			System.err.println("FOUTIEF COMMANDO ONTVANGEN: " + command);
		}
	}

	private static int[] getCoordsFromString(String coord) {
		String[] coords = Pattern.compile(",").split(coord);
		int[] result = new int[2];
		result[0] = Integer.parseInt(coords[0]);
		result[1] = Integer.parseInt(coords[1]);
		return result;
	}

	/**
	 * Partitions a string received by RabbitMQ. Standard delimiter is " "
	 */
	private String[] partitionStringCommand(String input) {
		input = (String) input.replace("\n", ""); // \n op einde
																	// verwijderen
		String[] parts = Pattern.compile(" +").split(input);
		return parts;
	}

	/**
	 * Partitions a line from a txt file. Standard delimiter is " "
	 */
	private static String[] partitionStringFile(String input) {
		input = (String) input.replace("\n", ""); // \n op einde
																// verwijderen
		String[] parts = Pattern.compile(" +").split(input);
		return parts;
	}

	public Game getGame() {
		return game;
	}

	private void setGame(Game game) {
		this.game = game;
	}
}
